
// Copyright (C) 2011 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//  
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//  
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;

namespace Derm.Render
{
	/// <summary>
	/// Service which create resources.
	/// </summary>
	public class RenderCreatorService : RenderKernelService
	{
		#region Constructors

		/// <summary>
		/// Construct a RenderLoaderService.
		/// </summary>
		public RenderCreatorService() : base(ThisServiceName, ExecutionModel.MultithreadContext)
		{
		
		}

		private static readonly string ThisServiceName = "Derm.Render.RenderCreatorService";

		#endregion

		#region Creation Task Queue

		/// <summary>
		/// Enqueue a IRenderResource for loading.
		/// </summary>
		/// <param name="rResource">
		/// A <see cref="IRenderResource"/> enqueue for creation.
		/// </param>
		/// <exception cref="ArgumentNullException">
		/// This exception is thrown if the parameter <paramref name="rResource"/> is null.
		/// </exception>
		public void EnqueueCreationTask(IRenderResource rResource)
		{
			if (rResource == null)
				throw new ArgumentNullException("rResource");

			lock (mPendingCreationsLock) {
				mPendingCreations.Enqueue(rResource);
			}
			mPendingCreationsSemaphore.Release();
		}

		/// <summary>
		/// Work queue.
		/// </summary>
		private Queue<IRenderResource> mPendingCreations = new Queue<IRenderResource>();

		/// <summary>
		/// Semaphore for scheduling this service.
		/// </summary>
		private System.Threading.Semaphore mPendingCreationsSemaphore = new System.Threading.Semaphore(0, 65536);

		/// <summary>
		/// Object used for synchronizating <see cref="mPendingCreations"/>.
		/// </summary>
		private object mPendingCreationsLock = new object();

		#endregion

		#region Service Access

		/// <summary>
		/// Get the RenderCreatorService for the specified object name space.
		/// </summary>
		/// <param name="rObjectNameSpace">
		/// A <see cref="System.Guid"/>
		/// </param>
		/// <returns>
		/// It returns a RenderCreatorService which handle objects generated in the name space <paramref name="rObjectNameSpace"/>.
		/// </returns>
		public static RenderCreatorService GetService(Guid rObjectNameSpace)
		{
			return ((RenderCreatorService)GetService(rObjectNameSpace, ThisServiceName));
		}

		#endregion

		#region Logging

		/// <summary>
		/// Logger of this class.
		/// </summary>
		private static readonly ILogger sLog = Log.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

		#endregion

		#region RenderKernelService Overrides

		/// <summary>
		/// Determine whether the executing thread is this service.
		/// </summary>
		public override bool IsCurrent { get { return (mIsCurrent); } }

		/// <summary>
		/// Execute this service is a separate thread.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> available for the executing thread.
		/// </param>
		protected override void ThreadExecute(RenderContext ctx)
		{
			const int SERVICE_TIMEOUT = 750;

			if (ctx == null)
				throw new ArgumentNullException("ctx");
			if (ctx.IsCurrent() == false)
				throw new ArgumentException("not current", "ctx");

			// On this thread, the RenderCreatorService is current.
			mIsCurrent = true;

			while (TestCancel == false) {
				if (mPendingCreationsSemaphore.WaitOne(SERVICE_TIMEOUT) == true) {
					IRenderResource renderResource;

					lock (mPendingCreationsLock) {
						renderResource = mPendingCreations.Dequeue();
					}

					try {
						// Execute creation of IRenderResource
						renderResource.Create(ctx);
					} catch (Exception exception) {
						sLog.Error("resource has failed the create stage", exception);
					}
				}
			}
		}

		/// <summary>
		/// Thread specific variable determining the service currency.
		/// </summary>
		[ThreadStatic()]
		private static bool mIsCurrent = false;

		#endregion
	}
}
